function mxCellHighlight(graph, highlightColor, strokeWidth)
{
	if (graph != null)
	{
		this.graph = graph;
		highlightColor = (highlightColor != null) ? highlightColor : mxConstants.DEFAULT_VALID_COLOR;
		strokeWidth = (strokeWidth != null) ? strokeWidth : mxConstants.HIGHLIGHT_STROKEWIDTH;
		this.shape = new mxRectangleShape(new mxRectangle(), null, highlightColor, strokeWidth);

		if (mxClient.IS_GC || mxClient.IS_SF || mxClient.IS_OP)
		{

			this.shape.dialect = mxConstants.DIALECT_STRICTHTML;
			this.shape.init(this.graph.container);
			mxEvent.redirectMouseEvents(this.shape.node, this.graph, null, null, true, true, true);
		}
		else
		{
			this.shape.dialect = (graph.dialect != mxConstants.DIALECT_SVG)
				? mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
			this.shape.init(graph.getView().getOverlayPane());
			var self = this;
			mxEvent.addListener(this.shape.node, 'mousedown', function(evt)
			{
				graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt, self.state));
			});
			mxEvent.addListener(this.shape.node, 'mousemove', function(evt)
			{
				graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt, self.state));
			});
			mxEvent.addListener(this.shape.node, 'mouseup', function(evt)
			{
				graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt, self.state));
			});
		}
		this.shape.node.style.display = 'none';
		this.edgeShape = new mxPolyline(
		[
			new mxPoint(),
			new mxPoint()
		], highlightColor, strokeWidth);

		this.edgeShape.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG)
			? mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
		this.edgeShape.init(graph.getView().getOverlayPane());
		this.edgeShape.node.style.display = 'none';
		mxEvent.addListener(this.edgeShape.node, 'mousedown', function(evt)
		{
			graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt, self.state));
		});
		mxEvent.addListener(this.edgeShape.node, 'mousemove', function(evt)
		{
			graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt, self.state));
		});
		mxEvent.addListener(this.edgeShape.node, 'mouseup', function(evt)
		{
			graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt, self.state));
		});
		var self = this;
		this.resetHandler = function(sender)
		{
			self.hide();
		};
		this.graph.getView().addListener(mxEvent.SCALE, this.resetHandler);
		this.graph.getView().addListener(mxEvent.TRANSLATE, this.resetHandler);
		this.graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, this.resetHandler);
		this.graph.getView().addListener(mxEvent.DOWN, this.resetHandler);
		this.graph.getView().addListener(mxEvent.UP, this.resetHandler);
		this.graph.getModel().addListener(mxEvent.CHANGE, this.resetHandler);
		this.setHighlightColor((highlightColor != null) ? highlightColor : mxConstants.DEFAULT_VALID_COLOR);
	}
};
mxCellHighlight.prototype.graph = true;
mxCellHighlight.prototype.state = null;
mxCellHighlight.prototype.resetHandler = null;
mxCellHighlight.prototype.setHighlightColor = function(color)
{
	if (this.shape.dialect == mxConstants.DIALECT_SVG)
	{
		this.shape.innerNode.setAttribute('stroke', color);
	}
	else if (this.shape.dialect == mxConstants.DIALECT_VML)
	{
		this.shape.node.setAttribute('strokecolor', color);
	}
	else
	{
		this.shape.node.style.borderColor = color;
	}

	if (this.edgeShape.dialect == mxConstants.DIALECT_SVG)
	{
		this.edgeShape.innerNode.setAttribute('stroke', color);
	}
	else
	{
		this.edgeShape.node.setAttribute('strokecolor', color);
	}
};
mxCellHighlight.prototype.hide = function()
{
	this.highlight();
};
mxCellHighlight.prototype.highlight = function(state)
{
	if (this.state != state)
	{
		if (state == null)
		{
			this.shape.node.style.display = 'none';
			this.edgeShape.node.style.display = 'none';
		}
		else
		{
			var shape = null;

			if (this.graph.model.isEdge(state.cell))
			{
				shape = this.edgeShape;
				shape.points = state.absolutePoints;
				this.shape.node.style.display = 'none';
			}
			else
			{
				shape = this.shape;
				var bounds = new mxRectangle(state.x, state.y, state.width, state.height);
				bounds.x -= 2;
				bounds.y -= 2;

				if (this.shape.dialect == mxConstants.DIALECT_STRICTHTML)
				{
					bounds.width -= 2;
					bounds.height -= 2;
				}
				else
				{
					bounds.width += 4;
					bounds.height += 4;
				}
				shape.bounds = bounds;
				this.edgeShape.node.style.display = 'none';
			}
			shape.node.style.display = 'inline';
			shape.redraw();

			if (shape == this.edgeShape)
			{
				mxUtils.repaintGraph(this.graph, shape.points[0]);
			}
		}
		this.state = state;
	}
};
mxCellHighlight.prototype.destroy = function()
{
	this.graph.getView().removeListener(this.resetHandler);
	this.graph.getModel().removeListener(this.resetHandler);

	if (this.shape != null)
	{
		this.shape.destroy();
		this.shape = null;
	}

	if (this.edgeShape != null)
	{
		this.edgeShape.destroy();
		this.edgeShape = null;
	}
};